package com.wg.net.client.http;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.wg.core.thread.fiber.IFiber;
import com.wg.core.thread.fiber.PoolFiber;

import java.util.Map;
import java.util.Objects;

/**
 * HTTP客户端工具类，支持同步/异步GET/POST请求，支持自定义请求头、表单数据、超时时间及异步执行纤维。
 *
 * @author 少爷123
 */
public class WgHttpClient {
    private static final Log log = LogFactory.get(WgHttpClient.class);
    private static final int DEFAULT_TIMEOUT_MS = 10000; // 默认超时时间10秒

    /**
     * 异步任务执行纤维（线程安全，构造后不可变）
     */
    private final IFiber defaultFiber;

    /**
     * 构造函数，使用默认纤维池
     */
    public WgHttpClient() {
        this(new PoolFiber());
    }

    /**
     * 构造函数，自定义异步执行纤维
     *
     * @param fiber 异步任务执行纤维（需已启动）
     */
    public WgHttpClient(IFiber fiber) {
        this.defaultFiber = Objects.requireNonNull(fiber, "fiber must not be null");
        if (!defaultFiber.isRunning()) {
            defaultFiber.start();
        }
    }

    // ------------------------------ 同步请求 ------------------------------

    /**
     * 同步发送GET请求
     *
     * @param url 请求URL
     * @return 响应内容（请求失败返回null）
     */
    public String get(String url) {
        return get(url, DEFAULT_TIMEOUT_MS);
    }

    /**
     * 同步发送GET请求（自定义超时）
     *
     * @param url     请求URL
     * @param timeout 超时时间（毫秒，-1表示使用底层默认超时）
     * @return 响应内容（请求失败返回null）
     */
    public String get(String url, int timeout) {
        try {
            return HttpUtil.get(url, timeout);
        } catch (Exception e) {
            log.error("同步GET请求失败 | URL: {}, 超时: {}ms, 异常: {}", url, timeout, e.getMessage());
            return null;
        }
    }

    /**
     * 同步发送POST请求（表单数据）
     *
     * @param url      请求URL
     * @param paramMap 表单参数（键值对）
     * @return 响应内容（请求失败返回null）
     */
    public String post(String url, Map<String, Object> paramMap) {
        return post(url, null, paramMap, DEFAULT_TIMEOUT_MS);
    }

    /**
     * 同步发送POST请求（表单数据+自定义超时）
     *
     * @param url      请求URL
     * @param paramMap 表单参数（键值对）
     * @param timeout  超时时间（毫秒，-1表示使用底层默认超时）
     * @return 响应内容（请求失败返回null）
     */
    public String post(String url, Map<String, Object> paramMap, int timeout) {
        return post(url, null, paramMap, timeout);
    }

    /**
     * 同步发送POST请求（自定义头+表单数据）
     *
     * @param url       请求URL
     * @param headerMap 请求头（键值对）
     * @param paramMap  表单参数（键值对）
     * @return 响应内容（请求失败返回null）
     */
    public String post(String url, Map<String, String> headerMap, Map<String, Object> paramMap) {
        return post(url, headerMap, paramMap, DEFAULT_TIMEOUT_MS);
    }

    /**
     * 同步发送POST请求（完整参数）
     *
     * @param url       请求URL
     * @param headerMap 请求头（键值对）
     * @param paramMap  表单参数（键值对）
     * @param timeout   超时时间（毫秒，-1表示使用底层默认超时）
     * @return 响应内容（请求失败返回null）
     */
    public String post(String url, Map<String, String> headerMap, Map<String, Object> paramMap, int timeout) {
        try {
            HttpRequest request = HttpRequest.post(url)
                    .timeout(timeout)
                    .form(paramMap);
            if (headerMap != null && !headerMap.isEmpty()) {
                request.addHeaders(headerMap);
            }
            return request.execute().body();
        } catch (Exception e) {
            log.error("同步POST请求失败 | URL: {}, 超时: {}ms, 表单参数: {}, 异常: {}",
                    url, timeout, paramMap, e.getMessage());
            return null;
        }
    }

    // ------------------------------ 异步请求 ------------------------------

    /**
     * 异步发送GET请求（使用默认纤维）
     *
     * @param url      请求URL
     * @param callback 响应回调
     */
    public void getAsync(String url, IResponseCallback callback) {
        getAsync(defaultFiber, url, DEFAULT_TIMEOUT_MS, callback);
    }

    /**
     * 异步发送GET请求（自定义超时+默认纤维）
     *
     * @param url      请求URL
     * @param timeout  超时时间（毫秒，-1表示使用底层默认超时）
     * @param callback 响应回调
     */
    public void getAsync(String url, int timeout, IResponseCallback callback) {
        getAsync(defaultFiber, url, timeout, callback);
    }

    /**
     * 异步发送GET请求（自定义纤维）
     *
     * @param fiber    异步执行纤维
     * @param url      请求URL
     * @param callback 响应回调
     */
    public void getAsync(IFiber fiber, String url, IResponseCallback callback) {
        getAsync(fiber, url, DEFAULT_TIMEOUT_MS, callback);
    }

    /**
     * 异步发送GET请求（完整参数）
     *
     * @param fiber    异步执行纤维（可为null，使用默认纤维）
     * @param url      请求URL
     * @param timeout  超时时间（毫秒，-1表示使用底层默认超时）
     * @param callback 响应回调
     */
    public void getAsync(IFiber fiber, String url, int timeout, IResponseCallback callback) {
        executeAsync(() -> {
            try {
                String result = HttpUtil.get(url, timeout);
                notifySuccess(callback, fiber, result);
            } catch (Exception e) {
                notifyError(callback, fiber, "GET请求失败: " + e.getMessage(), e);
            }
        }, "GET", url, timeout, null, null);
    }

    /**
     * 异步发送POST请求（表单数据，使用默认纤维）
     *
     * @param url      请求URL
     * @param paramMap 表单参数（键值对）
     * @param callback 响应回调
     */
    public void postAsync(String url, Map<String, Object> paramMap, IResponseCallback callback) {
        postAsync(defaultFiber, url, null, paramMap, DEFAULT_TIMEOUT_MS, callback);
    }

    /**
     * 异步发送POST请求（表单数据+自定义超时+默认纤维）
     *
     * @param url      请求URL
     * @param paramMap 表单参数（键值对）
     * @param timeout  超时时间（毫秒，-1表示使用底层默认超时）
     * @param callback 响应回调
     */
    public void postAsync(String url, Map<String, Object> paramMap, int timeout, IResponseCallback callback) {
        postAsync(defaultFiber, url, null, paramMap, timeout, callback);
    }

    /**
     * 异步发送POST请求（自定义头+表单数据，默认纤维）
     *
     * @param url       请求URL
     * @param headerMap 请求头（键值对）
     * @param paramMap  表单参数（键值对）
     * @param callback  响应回调
     */
    public void postAsync(String url, Map<String, String> headerMap, Map<String, Object> paramMap, IResponseCallback callback) {
        postAsync(defaultFiber, url, headerMap, paramMap, DEFAULT_TIMEOUT_MS, callback);
    }

    /**
     * 异步发送POST请求（完整参数）
     *
     * @param fiber     异步执行纤维（可为null，使用默认纤维）
     * @param url       请求URL
     * @param headerMap 请求头（键值对）
     * @param paramMap  表单参数（键值对）
     * @param timeout   超时时间（毫秒，-1表示使用底层默认超时）
     * @param callback  响应回调
     */
    public void postAsync(IFiber fiber, String url, Map<String, String> headerMap,
                          Map<String, Object> paramMap, int timeout, IResponseCallback callback) {
        executeAsync(() -> {
            try {
                HttpRequest request = HttpRequest.post(url)
                        .timeout(timeout)
                        .form(paramMap);
                if (headerMap != null && !headerMap.isEmpty()) {
                    request.addHeaders(headerMap);
                }
                String result = request.execute().body();
                notifySuccess(callback, fiber, result);
            } catch (Exception e) {
                notifyError(callback, fiber, "POST请求失败: " + e.getMessage(), e);
            }
        }, "POST", url, timeout, headerMap, paramMap);
    }

    // ------------------------------ 扩展支持（JSON） ------------------------------

    /**
     * 同步发送POST请求（JSON体）
     *
     * @param url     请求URL
     * @param json    JSON字符串
     * @param timeout 超时时间（毫秒，-1表示使用底层默认超时）
     * @return 响应内容（请求失败返回null）
     */
    public String postJson(String url, String json, int timeout) {
        try {
            return HttpRequest.post(url)
                    .timeout(timeout)
                    .body(json, "application/json")
                    .execute()
                    .body();
        } catch (Exception e) {
            log.error("同步POST JSON请求失败 | URL: {}, 超时: {}ms, JSON: {}, 异常: {}",
                    url, timeout, json, e.getMessage());
            return null;
        }
    }

    /**
     * 异步发送POST请求（JSON体）
     *
     * @param fiber    异步执行纤维（可为null，使用默认纤维）
     * @param url      请求URL
     * @param json     JSON字符串
     * @param timeout  超时时间（毫秒，-1表示使用底层默认超时）
     * @param callback 响应回调
     */
    public void postJsonAsync(IFiber fiber, String url, String json, int timeout, IResponseCallback callback) {
        executeAsync(() -> {
            try {
                String result = HttpRequest.post(url)
                        .timeout(timeout)
                        .body(json, "application/json")
                        .execute()
                        .body();
                notifySuccess(callback, fiber, result);
            } catch (Exception e) {
                notifyError(callback, fiber, "POST JSON请求失败: " + e.getMessage(), e);
            }
        }, "POST_JSON", url, timeout, null, null);
    }

    // ------------------------------ 私有工具方法 ------------------------------

    /**
     * 通用异步请求执行方法（抽象重复逻辑）
     *
     * @param task     异步任务逻辑
     * @param method   请求方法（用于日志）
     * @param url      请求URL
     * @param timeout  超时时间
     * @param headers  请求头
     * @param paramMap 表单参数
     */
    private void executeAsync(Runnable task, String method, String url, int timeout,
                              Map<String, String> headers, Map<String, Object> paramMap) {
        IFiber targetFiber = defaultFiber;
        if (targetFiber != null && !targetFiber.isRunning()) {
            log.warn("异步执行纤维未启动，使用当前线程执行任务 | 方法: {}, URL: {}", method, url);
        }

        Runnable asyncTask = () -> {
            try {
                task.run();
            } catch (Exception e) {
                log.error("{}请求异步执行异常 | 方法: {}, URL: {}, 异常: {}",
                        method, url, e.getMessage(), e);
                notifyError(null, targetFiber, "异步任务执行失败: " + e.getMessage(), e);
            }
        };

        if (targetFiber != null) {
            targetFiber.enqueue(asyncTask);
        } else {
            asyncTask.run(); // 降级到当前线程执行
        }
    }

    /**
     * 通知回调成功（线程安全）
     *
     * @param callback 回调接口
     * @param fiber    异步纤维（可为null，当前线程执行）
     * @param result   响应结果
     */
    private void notifySuccess(IResponseCallback callback, IFiber fiber, String result) {
        if (callback == null) return;

        Runnable successTask = () -> callback.success(result);
        executeCallback(successTask, fiber);
    }

    /**
     * 通知回调失败（线程安全）
     *
     * @param callback 回调接口
     * @param fiber    异步纤维（可为null，当前线程执行）
     * @param errorMsg 错误信息
     * @param e        异常对象（可选）
     */
    private void notifyError(IResponseCallback callback, IFiber fiber, String errorMsg, Exception e) {
        if (callback == null) return;

        Runnable errorTask = () -> callback.error(errorMsg);
        executeCallback(errorTask, fiber);
    }

    /**
     * 执行回调任务（确保在目标纤维或当前线程执行）
     *
     * @param task  回调任务
     * @param fiber 目标纤维（可为null，使用当前线程）
     */
    private void executeCallback(Runnable task, IFiber fiber) {
        if (fiber != null && fiber.isRunning()) {
            fiber.enqueue(task);
        } else {
            task.run(); // 降级到当前线程执行
        }
    }
}